/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.chigscommons.web.common.brix.plugin.menuml.tile;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.wicket.Response;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.model.IModel;
import org.apache.wicket.util.string.Strings;

import brix.auth.Action.Context;
import brix.jcr.wrapper.BrixNode;
import brix.plugin.site.SitePlugin;
import brix.web.generic.IGenericComponent;
import brix.web.nodepage.BrixNodeWebPage;
import brix.web.nodepage.BrixPageParameters;
import brix.web.reference.Reference;
import brix.web.reference.Reference.Type;

import com.googlecode.chigscommons.web.common.brix.plugin.menuml.MenuMultiLang;
import com.googlecode.chigscommons.web.common.brix.plugin.menuml.MenuMultiLang.ChildEntry;
import com.googlecode.chigscommons.web.common.brix.plugin.menuml.MenuMultiLang.Entry;

/**
 * Component used to render the menu
 * 
 * @author igor.vaynberg
 * 
 */
public class MenuMultiLangRenderer extends WebComponent implements IGenericComponent<BrixNode> {
    private static final long serialVersionUID = 1L;

    /**
     * Constructor
     * 
     * @param id
     * @param model
     */
    public MenuMultiLangRenderer(String id, IModel<BrixNode> model) {
        super(id, model);
    }

    /** {@inheritDoc} */
    @Override
    protected void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) {
        MenuMultiLangContainer container = new MenuMultiLangContainer();
        container.load(getModelObject());

        Set<ChildEntry> selected = getSelectedItems(container.getMenu());

        // how many levels to skip to start rendering
        int skipLevels = container.getStartAtLevel() != null ? container.getStartAtLevel() : 0;

        // how many levels should be rendered
        int renderLevels = container.getRenderLevels() != null
                ? container.getRenderLevels()
                : Integer.MAX_VALUE;

        Response response = getResponse();
        if (container.getMenu() != null) {
        renderEntry(container, container.getMenu().getRoot(), response, selected, skipLevels,
                renderLevels);
        }
    }

    private void renderEntry(MenuMultiLangContainer container, Entry entry, Response response,
            Set<ChildEntry> selected, int skipLevels, int renderLevels) {
        if (renderLevels <= 0) {
            return;
        }

        if (skipLevels <= 0) {
            boolean outer = skipLevels == 0;
            String klass = "";
            if (outer && !Strings.isEmpty(container.getOuterContainerStyleClass())) {
                klass = " class='" + container.getOuterContainerStyleClass() + "'";
            }
            else if (!outer && !Strings.isEmpty(container.getInnerContainerStyleClass())) {
                klass = " class='" + container.getInnerContainerStyleClass() + "'";
            }
            response.write("\n<ul");
            response.write(klass);
            response.write(">\n");
        }

        for (ChildEntry e : entry.getChildren()) {
            BrixNode node = getNode(e);
            if (node == null || SitePlugin.get().canViewNode(node, Context.PRESENTATION)) {
                renderChild(container, e, response, selected, skipLevels, renderLevels);
            }
        }

        if (skipLevels <= 0) {
            response.write("</ul>\n");
        }
    }

    private BrixNode getNode(ChildEntry entry) {
        if (entry.getReference() != null && !entry.getReference().isEmpty() &&
                entry.getReference().getType() == Type.NODE) {
            return entry.getReference().getNodeModel().getObject();
        }
        else {
            List<ChildEntry> children = entry.getChildren();
            if (children != null && !children.isEmpty()) {
                return getNode(children.iterator().next());
            }
            else {
                return null;
            }
        }
    }

    private String getUrl(ChildEntry entry) {
        if (entry.getReference() != null && !entry.getReference().isEmpty()) {
            return entry.getReference().generateUrl();
        }
        else {
            List<ChildEntry> children = entry.getChildren();
            if (children != null && !children.isEmpty()) {
                return getUrl(children.iterator().next());
            }
            else {
                return "#";
            }
        }
    }

    private boolean anyChildSelected(ChildEntry entry, Set<ChildEntry> selectedSet) {
        if (entry.getChildren() != null) {
            for (ChildEntry e : entry.getChildren()) {
                if (selectedSet.contains(e)) {
                    return true;
                }
            }
        }
        return false;
    }

    private void renderChild(MenuMultiLangContainer container, ChildEntry entry, Response response,
            Set<ChildEntry> selectedSet, int skipLevels, int renderLevels) {
        boolean selected = selectedSet.contains(entry);

        boolean anyChildren = selected && anyChildren(entry);

        if (skipLevels <= 0) {
            String listItemCssClass = "";
            String anchorCssClass = "";
            if (selected && !Strings.isEmpty(container.getSelectedItemStyleClass())) {
                listItemCssClass = container.getSelectedItemStyleClass();
                anchorCssClass = container.getSelectedItemStyleClass();
            }

            if (anyChildren && selected && anyChildSelected(entry, selectedSet) &&
                    !Strings.isEmpty(container.getItemWithSelectedChildStyleClass())) {
                listItemCssClass = container.getItemWithSelectedChildStyleClass();
            }

            if (!Strings.isEmpty(entry.getCssClass())) {
                if (!Strings.isEmpty(listItemCssClass)) {
                    listItemCssClass += " ";
                }
                listItemCssClass += entry.getCssClass();
            }

            response.write("\n<li");

            if (!Strings.isEmpty(listItemCssClass)) {
                response.write(" class=\"");
                response.write(listItemCssClass);
                response.write("\"");
            }

            response.write(">");

            final String url = getUrl(entry);

            response.write("<a");
            if (!Strings.isEmpty(anchorCssClass)) {
                response.write(" class=\"");
                response.write(anchorCssClass);
                response.write("\"");
            }
            response.write(" href=\"");
            response.write(url);
            response.write("\"><span>");

            // TODO. escape or not (probably a property would be nice?
            if (getLocale().equals(container.getMenu().getOtherLocale())) {
            	response.write(entry.getTitleOther());            	
            } else {
            	response.write(entry.getTitle());
            }

            response.write("</span></a>");
        }

        // only decrement skip levels for child if current is begger than 0
        int childSkipLevels = skipLevels - 1;

        // only decrement render levels when we are already rendering
        int childRenderLevels = skipLevels <= 0 ? renderLevels - 1 : renderLevels;

        if (anyChildren) {
            renderEntry(container, entry, response, selectedSet, childSkipLevels, childRenderLevels);
        }

        if (skipLevels == 0) {
            response.write("</li>\n");
        }
    }

    private boolean anyChildren(ChildEntry entry) {
        if (entry.getChildren() != null) {
            for (ChildEntry e : entry.getChildren()) {
                BrixNode node = getNode(e);
                if (node == null || SitePlugin.get().canViewNode(node, Context.PRESENTATION)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isSelected(Reference reference, String url) {
    	boolean eq = false;
        
        if (getPage() instanceof BrixNodeWebPage) {

            BrixNodeWebPage page = (BrixNodeWebPage)getPage();

            if (reference.getType() == Type.NODE) {
                eq = page.getModel().equals(reference.getNodeModel()) &&
                        comparePageParameters(page.getBrixPageParameters(), reference
                                .getParameters());

            } else {
                eq = url.equals(reference.getUrl()) &&
                        comparePageParameters(page.getBrixPageParameters(), reference
                                .getParameters());
            }
        }
        return eq;
    }

    private boolean comparePageParameters(BrixPageParameters page, BrixPageParameters fromReference) {
        if (fromReference == null ||
                (fromReference.getIndexedParamsCount() == 0 && fromReference.getQueryParamKeys()
                        .isEmpty())) {
            return true;
        }
        else {
            return BrixPageParameters.equals(page, fromReference);
        }
    }

    private boolean isSelected(ChildEntry entry) {
        final String url = "/" + getRequest().getPath();
        Reference ref = entry.getReference();
        if (ref == null) {
            return false;
        }
        else {
            return isSelected(ref, url);
        }
    }

    private void checkSelected(ChildEntry entry, Set<ChildEntry> selectedSet) {
        if (isSelected(entry)) {
            for (Entry e = entry; e instanceof ChildEntry; e = e.getParent()) {
                selectedSet.add((ChildEntry)e);
            }
        }
        for (ChildEntry e : entry.getChildren()) {
            checkSelected(e, selectedSet);
        }
    }

    Set<ChildEntry> getSelectedItems(MenuMultiLang menu) {
        Set<ChildEntry> result = new HashSet<ChildEntry>();
        if (menu == null) return result;
        for (ChildEntry e : menu.getRoot().getChildren()) {
            checkSelected(e, result);
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public IModel<BrixNode> getModel() {
        return (IModel<BrixNode>)getDefaultModel();
    }

    public BrixNode getModelObject() {
        return (BrixNode)getDefaultModelObject();
    }

    public void setModel(IModel<BrixNode> model) {
        setDefaultModel(model);
    }

    public void setModelObject(BrixNode object) {
        setDefaultModelObject(object);
    }
}
